/**************************************************************************************************
  Filename:       Dataservice.c
  Revised:        $Date: 2013-05-06 13:33:47 -0700 (Mon, 06 May 2013) $
  Revision:       $Revision: 34153 $

  Description:    Data Profiles.
**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"
#include "gapbondmgr.h"
#include "hal_drivers.h"

#include "peripheral.h"

#include "dataservice.h"


/*********************************************************************
 * CONSTANTS
 */
#define DATA_PACK_VALUE_POS            2
#define DATA_PACK_CHAR_CONFIG_POS      3
#ifdef GATT_TI_UUID_128_BIT

// TI Base 128-bit UUID: F000XXXX-0451-4000-B000-000000000000
#define TI_UUID_SIZE        ATT_UUID_SIZE
#define TI_UUID(uuid)       TI_BASE_UUID_128(uuid)

#else

// Using 16-bit UUID
#define TI_UUID_SIZE        ATT_BT_UUID_SIZE
#define TI_UUID(uuid)       LO_UINT16(uuid), HI_UINT16(uuid)

#endif
/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// Data GATT Profile Service UUID
CONST uint8 dataServUUID[TI_UUID_SIZE] =
{
  TI_UUID(DATA_SERV_UUID)
};
// Data Characteristic UUID
CONST uint8 dataPackUUID[TI_UUID_SIZE] =
{
  TI_UUID(DATA_PACK_UUID)
};
// Config Characteristic UUID
CONST uint8 dataAckUUID[TI_UUID_SIZE] =
{
  TI_UUID(DATA_ACK_UUID)
};

CONST uint8 dataCtrlUUID[TI_UUID_SIZE] =
{
  TI_UUID(DATA_CTRL_UUID)
};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static dataCBs_t *data_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables
 */

// Data Profile Service attribute
static CONST gattAttrType_t dataService = { ATT_BT_UUID_SIZE, dataServUUID };

// Data Profile Data Characteristic Properties
static uint8 dataPackProps = GATT_PROP_READ | GATT_PROP_NOTIFY | GATT_PROP_WRITE;

// Data Profile Data Characteristic Value
static uint8 dataPack[DATA_PACK_LEN] = {0,0,0,0, \
                                        0,0,0,0, \
                                        0,0,0,0, 0};
// #define DATA_SERV_UUID                  0xAA50 // 0000FFF0-0451-4000-B000-00000000-0000
// #define DATA_PACK_UUID                  0xAA51
// #define DATA_ACK_UUID                   0xAA52
// #define DATA_CTRL_UUID                  0xAA53
// Data Characteristic Configuration
static gattCharCfg_t dataPackConfig[GATT_MAX_NUM_CONN];

// Data Profile Data Characteristic User Description
static uint8 dataPackUserDesp[] = "Data Package\0";

// Data Profile Config Characteristic Properties
static uint8 dataAckProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Data Profile Config Characteristic Value
static uint8 dataAck = 0x00;

// Data Profile Config Characteristic User Description
static uint8 dataAckUserDesp[] = "Data Ack\0";

// Data Profile Config Characteristic Properties
static uint8 dataCtrlProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Data Profile Config Characteristic Value
static uint8 dataCtrl = 0x00;

// Data Profile Config Characteristic User Description
static uint8 dataCtrlUserDesp[] = "Data Ctrl\0";


/*********************************************************************
 * Profile Attributes - Table
 */
static gattAttribute_t dataAttrTbl[] =
{
  // Data Profile Service
  {
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&dataService                     /* pValue */
  },

    // Data Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &dataPackProps
    },

      // Data Characteristic Value
      {
        { TI_UUID_SIZE, dataPackUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        //0, // 貌似得用0
        0,
        dataPack
      },

      // Data Characteristic configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *)dataPackConfig
      },
      
      // Data Characteristic User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        dataPackUserDesp
      },
      
    // Ack Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &dataAckProps
    },

      // Ack Characteristic Value
      {
        { TI_UUID_SIZE, dataAckUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        &dataAck
      },

      // Ack Characteristic User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        dataAckUserDesp
      },

    // CTRL Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &dataCtrlProps
    },

      // CTRL Characteristic Value
      {
        { TI_UUID_SIZE, dataCtrlUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        &dataCtrl
      },

      // CTRL Characteristic User Description
      {
        { ATT_BT_UUID_SIZE, charUserDescUUID },
        GATT_PERMIT_READ,
        0,
        dataCtrlUserDesp
      },
};


/*********************************************************************
 * LOCAL FUNCTIONS
 */
static uint8 Data_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen );
static bStatus_t Data_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset );
static void Data_HandleConnStatusCB( uint16 connHandle, uint8 changeType );

/*********************************************************************
 * PROFILE CALLBACKS
 */

// Data Profile Service Callbacks
CONST gattServiceCBs_t dataCBs =
{
  Data_ReadAttrCB,  // Read callback function pointer
  Data_WriteAttrCB, // Write callback function pointer
  NULL              // Authorization callback function pointer
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      Data_AddService
 *
 * @brief   Initializes the Data Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t Data_AddService( uint32 services )
{
  uint8 status = SUCCESS;

  // Initialize Client Characteristic Configuration attributes
  GATTServApp_InitCharCfg( INVALID_CONNHANDLE, dataPackConfig );

  // Register with Link DB to receive link status change callback
  VOID linkDB_Register( Data_HandleConnStatusCB );

  if ( services & DATA_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( dataAttrTbl,
                                          GATT_NUM_ATTRS( dataAttrTbl ),
                                          &dataCBs );
  }

  return ( status );
}

/*********************************************************************
 * @fn      Data_RegisterAppCBs
 *
 * @brief   Registers the application callback function. Only call
 *          this function once.
 *
 * @param   callbacks - pointer to application callbacks.
 *
 * @return  SUCCESS or bleAlreadyInRequestedMode
 */
bStatus_t Data_RegisterAppCBs( dataCBs_t *appCallbacks )
{
  if ( data_AppCBs == NULL )
  {
    if ( appCallbacks != NULL )
    {
      data_AppCBs = appCallbacks;
    }

    return ( SUCCESS );
  }

  return ( bleAlreadyInRequestedMode );

}

/*********************************************************************
 * @fn      Data_SetParameter
 *
 * @brief   Set a Data Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t Data_EnableNotify(uint16 centBLEConnHandle){
    if (centBLEConnHandle == GAP_CONNHANDLE_INIT)
    {
      dbg("Failure\n");
      return 0;
    }
    dbg("Success\n");
    uint8 status = GATTServApp_WriteCharCfg( centBLEConnHandle, dataPackConfig, 0x01);
    return status;
}
bStatus_t Data_SetParameter( uint8 param, uint8 len, void *value )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {
    case DATA_PACK:
      if ( len == DATA_PACK_LEN )
      {
        VOID osal_memcpy( dataPack, value, DATA_PACK_LEN );

        uint16 gapConnHandle;
        GAPRole_GetParameter( GAPROLE_CONNHANDLE, &gapConnHandle );
        uint16 valueCfg = GATTServApp_ReadCharCfg( gapConnHandle, dataPackConfig );

        // If notifications enabled
        if ( valueCfg & GATT_CLIENT_CFG_NOTIFY )
        {
          attHandleValueNoti_t noti;
          noti.handle = dataAttrTbl[DATA_PACK_VALUE_POS].handle;
          noti.len = DATA_PACK_LEN;
          VOID osal_memcpy( noti.value,dataPack, DATA_PACK_LEN );
          
          GATT_Notification( gapConnHandle, &noti, FALSE );
        }
        // See if Notification has been enabled
        // GATTServApp_ProcessCharCfg(dataPackConfig , dataPack, FALSE,
        //                            dataAttrTbl, GATT_NUM_ATTRS( dataAttrTbl ),
        //                            INVALID_TASK_ID );
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;

    case DATA_ACK:
      if(len == sizeof ( uint8 ) )
      {
        dataAck = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
    case DATA_CTRL:
      if(len == sizeof ( uint8 ) )
      {
        dataCtrl = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn      DataProfile_GetParameter
 *
 * @brief   Get a Data Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to put.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16 will be cast to
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t Data_GetParameter( uint8 param, void *value )
{
  bStatus_t ret = SUCCESS;

  switch ( param )
  {
    case DATA_PACK:
      VOID osal_memcpy (value, dataPack, DATA_PACK_LEN );
      break;

    case DATA_ACK:
      *((uint8*)value) = dataAck;
      break;

    case DATA_CTRL:
      *((uint8*)value) = dataCtrl;
      break;

    default:
      ret = INVALIDPARAMETER;
      break;
  }

  return ( ret );
}

/*********************************************************************
 * @fn          Data_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static uint8 Data_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
{
  uint16 uuid;
  bStatus_t status = SUCCESS;

 /* // If attribute permissions require authorization to read, return error*/
  if ( gattPermitAuthorRead( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }

  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }
/*
  if (utilExtractUuid16(pAttr,&uuid) == FAILURE)
  {
    // Invalid handle
    return ATT_ERR_INVALID_HANDLE;
  }
*/
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {    
    // 16-bit UUID
    uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;
      // gattserverapp handles those reads
      case DATA_PACK_UUID:
        *pLen = DATA_PACK_LEN;
        VOID osal_memcpy( pValue, pAttr->pValue, DATA_PACK_LEN );
        break;

      case DATA_ACK_UUID:
        *pLen = 1;
        pValue[0] = *pAttr->pValue;
        break;

      case DATA_CTRL_UUID:
        *pLen = 1;
        pValue[0] = *pAttr->pValue;
        break;

      default:
        // Should never get here!
        *pLen = 0;
        status = ATT_ERR_ATTR_NOT_FOUND;
        break;
    }
  }
  else
  {
    // 128-bit UUID
    *pLen = 0;
    status = ATT_ERR_INVALID_HANDLE;
  }

  return ( status );
}

/*********************************************************************
 * @fn      data_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
static bStatus_t Data_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset )
{
  bStatus_t status = SUCCESS;
  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
  switch ( uuid )
  {
    case GATT_CLIENT_CHAR_CFG_UUID:
      if ( pAttr->handle == dataAttrTbl[DATA_PACK_CHAR_CONFIG_POS].handle )
      {
        status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                                offset, GATT_CLIENT_CFG_NOTIFY );
        if ( status == SUCCESS )
        {
          uint16 value = BUILD_UINT16( pValue[0], pValue[1] );
          // Notify application 
          data_AppCBs->pfnDatahange( (value == GATT_CFG_NO_OPERATION) ? DATA_PACK_CONFIG_NO_OP : DATA_PACK_CONFIG_SET_NOTIFY);
          // Todo if get DATA_PACK_CONFIG_SET_NOTIFY, set flag? what flag?
        } 
        else
        {
          status = ATT_ERR_INVALID_HANDLE;
        }
      }
      else
      {
        status = ATT_ERR_INVALID_VALUE_SIZE;
      }
      break;
  case DATA_PACK_UUID:
      // Validate the value
      // Make sure it's not a blob oper
      if ( offset == 0 )
      {
        if ( len > DATA_PACK_LEN )
        {
          status = ATT_ERR_INVALID_VALUE_SIZE;
        }
      }
      else
      {
        status = ATT_ERR_ATTR_NOT_LONG;
      }

      // Write the value
      if ( status == SUCCESS )
      {
        uint8 *pCurValue = (uint8 *)pAttr->pValue;
        // *pCurValue = *pValue; 
        VOID osal_memcpy( pCurValue, pValue, DATA_PACK_LEN ) ;
        
        data_AppCBs->pfnDatahange( DATA_PACK_RECEIVED );                //setting protocol
      }
      break;
  case DATA_ACK_UUID:
  case DATA_CTRL_UUID:
      // Validate the value
      // Make sure it's not a blob oper
      if ( offset == 0 )
      {
        if ( len != 1 )
        {
          status = ATT_ERR_INVALID_VALUE_SIZE;
        }
      }
      else
      {
        status = ATT_ERR_ATTR_NOT_LONG;
      }
      // Write the value
      if ( status == SUCCESS )
      {
        uint8 *pCurValue = (uint8 *)pAttr->pValue;
        *pCurValue = *pValue; 
        
        if(uuid == DATA_CTRL_UUID)
          data_AppCBs->pfnDatahange( DATA_CTRL_RECEIVED );       //notify application of write
        else
          data_AppCBs->pfnDatahange( DATA_ACK_RECEIVED );       //notify application of write
      }
      break;
      
    default:
      // Should never get here! (characteristics 2 and 4 do not have write permissions)
      status = ATT_ERR_ATTR_NOT_FOUND;
      break;
  }
  
  return ( status );
}

/*********************************************************************
 * @fn          Data_HandleConnStatusCB
 *
 * @brief       Data Profile link status change handler function.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
static void Data_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
           ( !linkDB_Up( connHandle ) ) ) )
    {
      GATTServApp_InitCharCfg( connHandle, dataPackConfig );
    }
  }
}


/*****************

@ return status:  Success
#define bleInvalidTaskID                INVALID_TASK  //!< Task ID isn't setup properly
#define bleNotReady                     0x10  //!< Not ready to perform task
#define bleAlreadyInRequestedMode       0x11  //!< Already performing that task
#define bleIncorrectMode                0x12  //!< Not setup properly to perform that task
#define bleMemAllocError                0x13  //!< Memory allocation error occurred
#define bleNotConnected                 0x14  //!< Can't perform function when not in a connection
#define bleNoResources                  0x15  //!< There are no resource available
#define blePending                      0x16  //!< Waiting
#define bleTimeout                      0x17  //!< Timed out performing function
#define bleInvalidRange                 0x18  //!< A parameter is out of range
#define bleLinkEncrypted                0x19  //!< The link is already encrypted
#define bleProcedureComplete            0x1A  //!< The Procedure is completed


*******************/
bStatus_t DataPack_Notify(uint16 connHandle,void *value)
{

    //uint16 valueCfg = GATTServApp_ReadCharCfg( connHandle, dataPackConfig );
    //dbg(":::::::::::%x",valueCfg);
    // If notifications enabled
    // if ( valueCfg & GATT_CLIENT_CFG_NOTIFY )
    // {
      attHandleValueNoti_t noti;
      noti.handle = dataAttrTbl[DATA_PACK_VALUE_POS].handle;
      noti.len = DATA_PACK_LEN;
      VOID osal_memcpy( noti.value,value,DATA_PACK_LEN );
      
      return GATT_Notification( connHandle, &noti, FALSE );
    // }
    //return bleIncorrectMode;
}
/*********************************************************************
*********************************************************************/
